using System;
using SunflowSharp.Core;
using SunflowSharp.Image;
using SunflowSharp.Maths;

namespace SunflowSharp.Core.Light
{

    public class DirectionalSpotlight : LightSource
    {
        private Point3 src;
        private Vector3 dir;
        private OrthoNormalBasis basis;
        private float r, r2;
        private Color radiance;

        public DirectionalSpotlight()
        {
            src = new Point3(0, 0, 0);
            dir = new Vector3(0, 0, -1);
            dir.normalize();
            basis = OrthoNormalBasis.makeFromW(dir);
            r = 1;
            r2 = r * r;
            radiance = Color.WHITE;
        }

        public bool update(ParameterList pl, SunflowAPI api)
        {
            src = pl.getPoint("source", src);
            dir = pl.getVector("dir", dir);
            dir.normalize();
            r = pl.getFloat("radius", r);
            basis = OrthoNormalBasis.makeFromW(dir);
            r2 = r * r;
            radiance = pl.getColor("radiance", radiance);
            return true;
        }

        public int getNumSamples()
        {
            return 1;
        }

        public int getLowSamples()
        {
            return 1;
        }

        public void getSamples(ShadingState state)
        {
            if (Vector3.dot(dir, state.getGeoNormal()) < 0 && Vector3.dot(dir, state.getNormal()) < 0)
            {
                // project point onto source plane
                float x = state.getPoint().x - src.x;
                float y = state.getPoint().y - src.y;
                float z = state.getPoint().z - src.z;
                float t = ((x * dir.x) + (y * dir.y) + (z * dir.z));
                if (t >= 0.0)
                {
                    x -= (t * dir.x);
                    y -= (t * dir.y);
                    z -= (t * dir.z);
                    if (((x * x) + (y * y) + (z * z)) <= r2)
                    {
                        Point3 p = new Point3();
                        p.x = src.x + x;
                        p.y = src.y + y;
                        p.z = src.z + z;
                        LightSample dest = new LightSample();
                        dest.setShadowRay(new Ray(state.getPoint(), p));
                        dest.setRadiance(radiance, radiance);
                        dest.traceShadow(state);
                        state.addSample(dest);
                    }
                }
            }
        }

        public void getPhoton(double randX1, double randY1, double randX2, double randY2, Point3 p, Vector3 dir, Color power)
        {
            float phi = (float)(2 * Math.PI * randX1);
            float s = (float)Math.Sqrt(1.0f - randY1);
            dir.x = r * (float)Math.Cos(phi) * s;
            dir.y = r * (float)Math.Sin(phi) * s;
            dir.z = 0;
            basis.transform(dir);
            Point3.add(src, dir, p);
            dir.set(this.dir);
            power.set(radiance).mul((float)Math.PI * r2);
        }

        public float getPower()
        {
            return radiance.copy().mul((float)Math.PI * r2).getLuminance();
        }
    }
}